<?php

class Lib_Upload
{
    private static $obj;
    // 文件存储路径
    private static $savePath;
    // 分片文件零时存储路径
    public static $tmpPath;
    // 上传文件类型
    private static $fileType = [
        'archive' => ['application/zip','application/octet-stream'],
        'pdf' => ['application/pdf'],
        'image' => ['image/jpeg', 'image/gif', 'image/png'],
        'xlsx' => ['application/vnd.openxmlformats-officedocument.spreadsheetml.sheet','application/octet-stream'],
    ];
    private static $commonUpladPath = null;
    private static $evidenceUpladPath = null;

    public function __construct(string $savePath = '')
    {
        self::$savePath = !empty($savePath) ? $savePath : realpath(LIBRARY_DIR . '/../') . '/resource_tmp/archive/';
        self::$commonUpladPath = realpath(LIBRARY_DIR . '/../') . '/resource/upload/';
        self::$evidenceUpladPath = realpath(LIBRARY_DIR . '/../') . '/evidencefiles/';
        self::$tmpPath = realpath(LIBRARY_DIR . '/../') . '/resource_tmp/chunks/';
    }

    /**
     * @return string
     */
    public function getTmpPath(): string
    {
        return self::$tmpPath;
    }

    /**
     * Undocumented function.
     *
     * @param string $savePath
     *
     * @return Lib_Upload
     */
    public static function getInstance(string $savePath = '')
    {
        if (is_null(self::$obj)) {
            self::$obj = new self($savePath);
        }

        return self::$obj;
    }

    /**
     * 单文件上传.
     *
     * @param array  $file
     * @param string $fileType
     *
     * @return array
     *
     * @throws Exception
     */
    public function SingleFile(array $file, string $fileType = 'archive'): array
    {
        $file_type = $file['type'];
        if (!in_array($file_type, self::$fileType[strtolower($fileType)])) {
            throw new \Exception('上传格式不支持'.$fileType);
        }
        $savePath = self::$savePath;
        static::checkDir($savePath);
        $filename =  time() . "_" . $file['name'];
        $filePath = $savePath .$filename;
        $this->checkFileExist($filePath);
        $this->moveFile($file['tmp_name'], $filePath);

        return [$filename, $savePath . $filename];
    }

    /**
     * 分块文件保存.
     *
     * @param string $identifier
     * @param array  $file
     * @param int    $chunkIndex
     *
     * @return string
     *
     * @throws Exception
     */
    public function chunkFile(string $identifier, array $file, int $chunkIndex): string
    {
        $savePath = self::$tmpPath . $identifier . '/';
        static::checkDir($savePath);
        static::moveFile($file['tmp_name'], $savePath . $chunkIndex);

        return $savePath;
    }

    /**
     * 目录检测.
     *
     * @param string $dirPath
     *
     * @throws Exception
     */
    public function checkDir(string $dirPath)
    {
        if (!is_dir($dirPath)) {
            if (!mkdir($dirPath, 0744, true)) {
                throw new \Exception('文件上传失败');
            }
        }
    }

    /**
     * 重复文件检测.
     *
     * @param string $filePath
     *
     * @return bool
     */
    public function checkFileExist(string $filePath): bool
    {
        return file_exists($filePath);
    }

    /**
     * @param string $tmpFile
     * @param string $filePath
     *
     * @throws Exception
     */
    private function moveFile(string $tmpFile, string $filePath)
    {
        $res = move_uploaded_file($tmpFile, $filePath);
        if (!$res) {
            throw new \Exception('文件上传失败');
        }
    }

    /**
     * 删除存储chunk 的临时目录.
     *
     * @param string $identifier
     */
    public function delDir(string $identifier)
    {
        $dir = self::$tmpPath . $identifier;
        if (is_dir($dir)) {
            $handle = opendir($dir); //读资源
            while (($file = readdir($handle)) !== false) {
                if ($file != '.' && $file != '..') {
                    unlink($dir . '/' . $file);
                }
            }
            rmdir($dir);
        }
    }

    public function deleteDir(string $path)
    {
        if (is_dir($path)) {
            $dh = opendir($path);
            while (($d = readdir($dh)) !== false) {
                if ($d == '.' || $d == '..') { //如果为.或..
                    continue;
                }
                $tmp = $path . '/' . $d;
                if (!is_dir($tmp)) { //如果为文件
                    unlink($tmp);
                } else { //如果为目录
                    $this->deleteDir($tmp);
                }
            }
            closedir($dh);
            rmdir($path);
        }
    }

    /**
     * 获取已上传的分片列表.
     *
     * @param string identifier
     *
     * @return array
     */
    public function getChunkList(string $identifier): array
    {
        $result = [];
        $dir = self::$tmpPath . $identifier;
        if (is_dir($dir)) {
            $handle = opendir($dir); //读资源
            if ($handle) {
                while (($file = readdir($handle)) !== false) {
                    if ($file != '.' && $file != '..') {
                        $result[] = intval($file);
                    }
                }
                closedir($handle);
            }
        }

        return $result;
    }

    /**
     * 普通文件上传.
     *
     * @param        $file
     * @param string $dir_name
     *
     * @return array
     */
    public function uploadFile($file, $dir_name = 'plaintiff',$evidence = 0,$fileName = '')
    {
        $data = ['code' => 0, 'msg' => 'success', 'data' => ''];
        if (!$file) {
            $data['code'] = 1;
            $data['msg'] = '文件不能为空';

            return $data;
        }
        $savePath = $evidence ? self::$evidenceUpladPath . $dir_name . '/' : self::$commonUpladPath . $dir_name . '/';
        try {
            static::checkDir($savePath);
        } catch (Exception $e) {
            $data['code'] = 1;
            $data['msg'] = $e->getMessage();

            return $data;
        }
        if($fileName){
            $fileName = $fileName .'.'. substr(strrchr($file['name'], '.'), 1);
        }else{
            $fileName = time() . rand(1, 1000) . $file['name'];
        }
        $filePath = $savePath . $fileName;
        try {
            $this->moveFile($file['tmp_name'], $filePath);
        } catch (Exception $e) {
            $data['code'] = 1;
            $data['msg'] = $e->getMessage();

            return $data;
        }
        $data['data'] = '/' . basename(self::$commonUpladPath) . '/' . $dir_name . '/' . $fileName;

        return $data;
    }
}
